package gu.simplemq.redis;

import static com.google.common.base.Preconditions.*;
import static redis.clients.jedis.Protocol.*;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
import com.google.common.base.Predicates;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.net.HostAndPort;

import gu.simplemq.redis.JedisPoolLazy.PropName;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Protocol;
import redis.clients.jedis.exceptions.JedisException;
import redis.clients.util.JedisURIHelper;

/**
 * @author guyadong
 *
 */
public class JedisUtils {
	private JedisUtils() {}

	/** 
	 * 根据{@code props}提供的参数及缺省参数{@link DEFAULT_PARAMETERS}创建一组完整的初始化参数<br>
	 * 如果props中定义了uri，则优先使用uri提供的参数
	 * @param props
	 */
	public static HashMap<JedisPoolLazy.PropName,Object> initParameters(Map<JedisPoolLazy.PropName,Object> props){
		// 初始化时复制一份缺省参数
		HashMap<JedisPoolLazy.PropName,Object> params = Maps.newHashMap(JedisPoolLazy.DEFAULT_PARAMETERS);
		if(null != props){			
			// 过滤掉所有为null的参数，避免将缺省参数覆盖为null
			Map<JedisPoolLazy.PropName, Object> filtered = Maps.filterValues(props, Predicates.notNull());
			// 缺省参数与输入参数合并
			params.putAll(filtered);
			URI uri = (URI) props.get(JedisPoolLazy.PropName.uri);
			if(uri != null){
				uri = new JedisURI(uri).getUri();
				params.put(JedisPoolLazy.PropName.uri, uri);
				if(uri.getHost() != null){
					params.put(JedisPoolLazy.PropName.host, uri.getHost());
				}
				if(uri.getPort() != -1){
					params.put(JedisPoolLazy.PropName.port, uri.getPort());
				}
				int dbIndex = JedisURIHelper.getDBIndex(uri);
				if(dbIndex != 0){
					params.put(JedisPoolLazy.PropName.database, dbIndex);
				}
				String pwd = JedisURIHelper.getPassword(uri);
				if(pwd != null){
					params.put(JedisPoolLazy.PropName.password, pwd);
				}
			}
		}
		return params;
	}
	public static HashMap<JedisPoolLazy.PropName,Object> asRedisParameters(Map<String,Object> props){
		props = MoreObjects.firstNonNull(props, Collections.<String,Object>emptyMap());
		HashMap<JedisPoolLazy.PropName,Object> map = Maps.newHashMap();
		for(Entry<String, Object> entry:props.entrySet()){
			PropName prop;
			try {
				prop = JedisPoolLazy.PropName.valueOf(entry.getKey());
			} catch (RuntimeException e) {
				// string转换失败则继续循环
				continue;
			}
			map.put(prop,prop.parse(entry.getValue()));
		}
		return map;
	}
	public static HashMap<JedisPoolLazy.PropName,Object> asRedisParameters2(Map<String,String> props){
		return asRedisParameters((Maps.transformValues(props, new Function<String,Object>(){
			@Override
			public Object apply(String input) {
				return input;
			}})));
	}
	/**
	 * 将类型为{@code Map<JedisPoolLazy.PropName,Object>}的参数转换为{@code HashMap<String,Object>},
	 * key为枚举变量名
	 * @param props
	 * @return HashMap<String,Object>实例，props为{@code null}时返回空实例
	 */
	public static HashMap<String,Object> asMqParameters(Map<JedisPoolLazy.PropName,Object> props){
		props = MoreObjects.firstNonNull(props, Collections.<JedisPoolLazy.PropName,Object>emptyMap());
		HashMap<String,Object> map = Maps.newHashMap();
		for(Entry<JedisPoolLazy.PropName, Object> entry:props.entrySet()){
			try {
				map.put(entry.getKey().name(),entry.getValue());
			} catch (Throwable e) {
			}
		}
		return map;		
	}
	/**
	 * 将类型为{@code Map<JedisPoolLazy.PropName,Object>}的参数转换为{@code HashMap<String,String>},
	 * key为枚举变量名
	 * @param props
	 * @return HashMap<String,String>实例，props为{@code null}时返回空实例
	 */
	public static HashMap<String,String> asMqParameters2(Map<JedisPoolLazy.PropName,Object> props){
		props = MoreObjects.firstNonNull(props, Collections.<JedisPoolLazy.PropName,Object>emptyMap());
		HashMap<String,String> map = Maps.newHashMap();
		for(Entry<JedisPoolLazy.PropName, Object> entry:props.entrySet()){
			try {
				PropName prop = entry.getKey();
				map.put(prop.name(),prop.toValueString(entry.getValue()));
			} catch (Throwable e) {
			}
		}
		return map;		
	}
	/** 
	 * 根据{@code props}提供的参数及缺省参数{@link DEFAULT_PARAMETERS}创建一组完整的redis初始化参数<br>
	 * 如果props中定义了uri，则优先使用uri提供的参数
	 * @param props
	 */
	public static HashMap<JedisPoolLazy.PropName,Object> initAsRedisParameters(Map<String,Object> props){
		HashMap<JedisPoolLazy.PropName, Object> map = asRedisParameters(props);
		return initParameters(map);
	}
	/** 
	 * 根据{@code props}提供的参数及缺省参数{@link DEFAULT_PARAMETERS}创建一组完整的redis初始化参数<br>
	 * 如果props中定义了uri，则优先使用uri提供的参数
	 * @param props
	 */
	public static HashMap<JedisPoolLazy.PropName,Object> initAsRedisParameters2(Map<String,String> props){
		HashMap<JedisPoolLazy.PropName, Object> map = asRedisParameters2(props);
		return initParameters(map);
	}
	
	public static HostAndPort getHostAndPort(Map<PropName, Object> parameters){
		String host;
		int port;
		checkArgument(null != parameters,"parameters is null");
		if(parameters.get(PropName.uri) != null){
			URI uri = (URI)parameters.get(PropName.uri);
			host = uri.getHost();
			port = uri.getPort();
		}else {
			host = (String) checkNotNull(parameters.get(PropName.host),"NO DEFINED %s",PropName.host);
			port  = ((Number) checkNotNull(parameters.get(PropName.port),"NO DEFINED %s",PropName.port)).intValue();	
		}
		return HostAndPort.fromParts(host, port);
	}
	/**
	 * 创建连接redis的 URI,如'jedis://:123@myhost:6666/0'<br>
	 * 如果host,port,database参数为{@code null}则用默认值代替
	 * {@link Protocol#DEFAULT_HOST},{@link Protocol#DEFAULT_PORT},{@link Protocol#DEFAULT_DATABASE}
	 * @param scheme Scheme name,'jedis' as default if null 
	 * @param host 主机名
	 * @param port 端口号
	 * @param password 没有密码设为空或{@code null}
	 * @param database 数据库
	 * @return {@link URI}实例
	 */
	public static URI createJedisURI(String scheme,String host, Integer port,String password,Integer database){
		String userInfo = null;
		if (!Strings.isNullOrEmpty(password)) {
			userInfo = ":" + password;
		}
		try {
			return new URI(MoreObjects.firstNonNull(scheme, "jedis"), userInfo,
					MoreObjects.firstNonNull(host,DEFAULT_HOST),
					MoreObjects.firstNonNull(port,DEFAULT_PORT),
					"/" + MoreObjects.firstNonNull(database,DEFAULT_DATABASE),	
					null,null);
		} catch (URISyntaxException e) {
			throw new RuntimeException(e);
		}	
	}
	/**
	 * 创建schema为'jedis'的URI,如'jedis://:123@myhost:6666/0'<br>
	 * @param host 主机名
	 * @param port 端口号
	 * @param password 没有密码设为空或{@code null}
	 * @param database 数据库
	 * @return {@link URI}实例
	 * @see #createJedisURI(String, String, Integer, String, Integer)
	 */
	public static URI createJedisURI(String host, Integer port,String password,Integer database){
		return createJedisURI("jedis",host,port,password,database);
	}
	/**
	 * 根据连接参数创建一个{@link URI}对象
	 * @param parameters
	 * @return
	 */
	public static URI getCanonicalURI(Map<JedisPoolLazy.PropName,Object> parameters){
		parameters = JedisUtils.initParameters(parameters);
		URI uri = (URI) parameters.get(JedisPoolLazy.PropName.uri);
		if (null == uri) {
			uri = createJedisURI((String) parameters.get(JedisPoolLazy.PropName.host),
					((Number) parameters.get(JedisPoolLazy.PropName.port)).intValue(),
					(String) parameters.get(JedisPoolLazy.PropName.password),
					((Number)parameters.get(JedisPoolLazy.PropName.database)).intValue());
		}else{
			uri = new JedisURI(uri).getUri();	
		}
		return uri;
	}
	/**
	 * 执行 redis操作,
	 * @param fun redis操作
	 * @param poolLazy
	 * @return
	 */
	public static <T> T runOnRedis(Function<Jedis,T> fun,JedisPoolLazy poolLazy) {
		if(null == fun){
			return null;
		}
		Jedis jedis = poolLazy.apply();
		try{
			return fun.apply(jedis);
		}finally{
			poolLazy.free();
		}
	}
	public static <T> T runOnRedis(Function<Jedis,T> fun) {
		return runOnRedis(fun,JedisPoolLazy.getDefaultInstance());
	}
	/**
	 * 执行 {@link Jedis#set(String, String)} 设置{@code key}的值{@code value}<br>
	 * {@code key,value}不可为{@code null}
	 * @param jedisPoolLazy
	 * @param key
	 * @param value
	 * @return
	 */
	public static String set(JedisPoolLazy jedisPoolLazy,final String key, final String value){
		checkArgument(!Strings.isNullOrEmpty(key) && !Strings.isNullOrEmpty(value),"key or value is null or empty");
		return runOnRedis(new Function<Jedis,String>(){
			@Override
			public String apply(Jedis input) {
				input.set(key, value);
				return input.get(key);
			}},jedisPoolLazy);
	}
	/**
	 * 执行 {@link Jedis#set(String, String)} 设置{@code key}的值{@code value}<br>
	 * {@code key,value}不可为{@code null}
	 * @param key
	 * @param value
	 * @return
	 */
	public static String set(String key, String value){
		return set(JedisPoolLazy.getDefaultInstance(),key,value);
	}
	/**
	 * 执行 {@link Jedis#setnx(String, String)} 设置{@code key}的值{@code value},如果{@code key}已经存在就返回原值<br>
	 * {@code key,value}不可为{@code null}
	 * @param jedisPoolLazy
	 * @param key
	 * @param value
	 * @return
	 */
	public static String setnx(JedisPoolLazy jedisPoolLazy,final String key, final String value){
		checkArgument(!Strings.isNullOrEmpty(key) && !Strings.isNullOrEmpty(value),"key or value is null or empty");
		return runOnRedis(new Function<Jedis,String>(){
			@Override
			public String apply(Jedis input) {
				input.setnx(key, value);
				return input.get(key);
			}},jedisPoolLazy);
	}
	/**
	 * 执行 {@link Jedis#setnx(String, String)} 设置{@code key}的值{@code value},如果{@code key}已经存在就返回原值<br>
	 * {@code key,value}不可为{@code null}
	 * @param key
	 * @param value
	 * @return
	 */
	public static String setnx(String key, String value){
		return setnx(JedisPoolLazy.getDefaultInstance(),key,value);
	}
	/**
	 * 执行{@link Jedis#pubsubNumSub(String...)}命令返回指定频道的订阅数量
	 * @param jedisPoolLazy
	 * @param channel 频道名
	 * @return 频道的订阅数量
	 */
	public static int pubsubNumSub(JedisPoolLazy jedisPoolLazy,final String channel){
		checkArgument(!Strings.isNullOrEmpty(channel),"key is null or empty");
		return runOnRedis(new Function<Jedis,Integer>(){
			@Override
			public Integer apply(Jedis input) {
				return Integer.parseInt(input.pubsubNumSub(channel).get(channel));
			}},jedisPoolLazy).intValue();
	}
	/**
	 * 执行{@link Jedis#pubsubNumSub(String...)}命令返回指定频道的订阅数量
	 * @param channel 频道名
	 * @return 频道的订阅数量
	 */
	public static int pubsubNumSub(String channel){
		return pubsubNumSub(JedisPoolLazy.getDefaultInstance(),channel);
	}
	/**
	 * 执行 {@link Jedis#get(String)} 返回{@code key}的值<br>
	 * @param jedisPoolLazy
	 * @param key 不可为{@code null}
	 * @return
	 */
	public static String get(JedisPoolLazy jedisPoolLazy,final String key){
		checkArgument(!Strings.isNullOrEmpty(key),"key is null or empty");
		return runOnRedis(new Function<Jedis,String>(){
			@Override
			public String apply(Jedis input) {
				return input.get(key);
			}},jedisPoolLazy);
	}
	/**
	 * 执行 {@link Jedis#get(String)} 返回{@code key}的值<br>
	 * @param key 不可为{@code null}
	 * @return
	 */
	public static String get(String key){
		return get(JedisPoolLazy.getDefaultInstance(),key);
	}
	/**
	 * 执行 {@link Jedis#keys(String)} 返回所有匹配{@code pattern}的key值<br>
	 * @param jedisPoolLazy
	 * @param pattern 不可为{@code null}
	 * @return
	 */
	public static Set<String> keys(JedisPoolLazy jedisPoolLazy,final String pattern){
		checkArgument(!Strings.isNullOrEmpty(pattern),"key is null or empty");
		return runOnRedis(new Function<Jedis,Set<String>>(){
			@Override
			public Set<String> apply(Jedis input) {
				return input.keys(pattern);
			}},jedisPoolLazy);
	}
	/**
	 * 执行 {@link Jedis#keys(String)} 返回所有匹配{@code pattern}的key值<br>
	 * @param pattern 不可为{@code null}
	 * @return
	 */
	public static Set<String> keys(String pattern){
		return keys(JedisPoolLazy.getDefaultInstance(),pattern);
	}
	/**
	 * 执行 {@link Jedis#del(String)} 返回被删除{@code key}的数量<br>
	 * @param jedisPoolLazy
	 * @param key 不可为{@code null}
	 * @return 返回被删除{@code key}的数量
	 */
	public static int del(JedisPoolLazy jedisPoolLazy,final String key){
		checkArgument(!Strings.isNullOrEmpty(key),"key is null or empty");
		return runOnRedis(new Function<Jedis,Long>(){
			@Override
			public Long apply(Jedis input) {
				return input.del(key);
			}},jedisPoolLazy).intValue();
	}
	/**
	 * 执行 {@link Jedis#del(String)}<br>
	 * @param key 不可为{@code null}
	 * @return 返回被删除{@code key}的数量
	 */
	public static int del(String key){
		return del(JedisPoolLazy.getDefaultInstance(),key);
	}

	/**
	 * 执行 {@link Jedis#del(String...)},忽略所有为{@code null}元素<br>
	 * @param jedisPoolLazy
	 * @param keys 为{@code null}或空忽略
	 * @return 返回被删除{@code key}的数量
	 */
	public static int del(JedisPoolLazy jedisPoolLazy,Iterable<String>keys){
		if(null != keys && !Iterables.isEmpty(keys)){
			/** 过滤所有的为null元素 */
			Iterable<String> filtered = Iterables.filter(keys, Predicates.notNull());
			if(!Iterables.isEmpty(filtered)){
				final String[] notNullKeys = Iterables.toArray(filtered, String.class); 
				return runOnRedis(new Function<Jedis,Long>(){
					@Override
					public Long apply(Jedis input) {
						return input.del(notNullKeys);
					}},jedisPoolLazy).intValue();			
			}
		}
		return 0;
	}
	/**
	 * 执行 {@link Jedis#get(String...)},忽略所有为{@code null}元素<br>
	 * @param keys 为{@code null}或空忽略
	 * @return 返回被删除记录的数量
	 */
	public static int del(Iterable<String> keys){
		return del(JedisPoolLazy.getDefaultInstance(),keys);
	}

	/**
	 * 执行 {@link Jedis#del(String...)},忽略所有为{@code null}元素<br>
	 * @param jedisPoolLazy
	 * @param keys 为{@code null}或空忽略
	 * @return 返回被删除记录的数量
	 */
	public static int del(JedisPoolLazy jedisPoolLazy,String... keys){
		if(null != keys && keys.length >0){
			return del(jedisPoolLazy,Arrays.asList(keys));
		}
		return 0;
	}
	/**
	 * 执行 {@link Jedis#get(String...)} ,忽略所有为{@code null}元素<br>
	 * @param keys 为{@code null}或空忽略
	 * @return 返回被删除记录的数量
	 */
	public static int del(String... keys){
		return del(JedisPoolLazy.getDefaultInstance(),keys);
	}
	/**
	 * 将redis中{@code key}指定的变量步进加1并返回
	 * @param jedisPoolLazy
	 * @param key 变量名,不可为{@code null}或空
	 * @return
	 * @see {@link Jedis#incr(String)}
	 */
	public static int incr(JedisPoolLazy jedisPoolLazy,final String key){
		checkArgument(!Strings.isNullOrEmpty(key),"key is null or empty");
		return runOnRedis(new Function<Jedis,Integer>(){
			@Override
			public Integer apply(Jedis input) {
				return input.incr(key).intValue();
			}},jedisPoolLazy);
	}
	/**
	 * 将redis中{@code key}指定的变量步进加1并返回
	 * @param key 变量名,不可为{@code null}或空
	 * @return
	 * @see {@link Jedis#incr(String)}
	 */
	public static int incr(String key){
		return incr(JedisPoolLazy.getDefaultInstance(), key);
	}
	
	/**
	 * 将redis中{@code key}指定的变量步进减1并返回
	 * @param jedisPoolLazy
	 * @param key 变量名,不可为{@code null}或空
	 * @return
	 * @see {@link Jedis#decr(String)}
	 */
	public static int decr(JedisPoolLazy jedisPoolLazy ,final String key){
		checkArgument(!Strings.isNullOrEmpty(key),"key is null or empty");
		return runOnRedis(new Function<Jedis,Integer>(){
			@Override
			public Integer apply(Jedis input) {
				return input.decr(key).intValue();
			}},jedisPoolLazy);
	}
	/**
	 * 将redis中{@code key}指定的变量步进减1并返回
	 * @param key 变量名,不可为{@code null}或空
	 * @return
	 * @see {@link Jedis#decr(String)}
	 */
	public static int decr(final String key){
		return decr(JedisPoolLazy.getDefaultInstance(), key);
	}
	/**
	 * 向redis中{@code key}指定的集合添加一个或多个成员
	 * @param jedisPoolLazy
	 * @param key 变量名,不可为{@code null}或空
	 * @param members 添加的成员
	 * @return
	 * @see {@link Jedis#sadd(String, String...)}
	 */
	public static int sadd(JedisPoolLazy jedisPoolLazy,final String key,final String... members){
		checkArgument(!Strings.isNullOrEmpty(key),"key is null or empty");
		checkArgument(members != null,"members is null ");
		return runOnRedis(new Function<Jedis,Integer>(){
			@Override
			public Integer apply(Jedis input) {
				return input.sadd(key, members).intValue();
			}},jedisPoolLazy);
	}
	/**
	 * 向redis中{@code key}指定的集合添加一个或多个成员
	 * @param key 变量名,不可为{@code null}或空
	 * @param members 添加的成员
	 * @return
	 * @see {@link Jedis#sadd(String, String...)}
	 */
	public static int sadd(String key,String... members){
		return sadd(JedisPoolLazy.getDefaultInstance(), key, members);
	}
	/**
	 * 移除redis中{@code key}指定的集合中一个或多个成员
	 * @param jedisPoolLazy
	 * @param key 变量名,不可为{@code null}或空
	 * @param members 移除的成员
	 * @return
	 * @see {@link Jedis#srem(String, String...)}
	 */
	public static int srem(JedisPoolLazy jedisPoolLazy,final String key,final String... members){
		checkArgument(!Strings.isNullOrEmpty(key),"key is null or empty");
		checkArgument(members != null,"members is null ");
		return runOnRedis(new Function<Jedis,Integer>(){
			@Override
			public Integer apply(Jedis input) {
				return input.srem(key, members).intValue();
			}},jedisPoolLazy);
	}
	/**
	 * 移除redis中{@code key}指定的集合中一个或多个成员
	 * @param key 变量名,不可为{@code null}或空
	 * @param members 移除的成员
	 * @return
	 * @see {@link Jedis#srem(String, String...)}
	 */
	public static int srem(final String key,final String... members){
		return srem(JedisPoolLazy.getDefaultInstance(),key,members);
	}
	/**
	 * 获取{@code key}指定的集合的成员数
	 * @param jedisPoolLazy
	 * @param key 变量名,不可为{@code null}或空
	 * @return
	 * @see {@link Jedis#scard(String)}
	 */
	public static int scard(JedisPoolLazy jedisPoolLazy,final String key){
		checkArgument(!Strings.isNullOrEmpty(key),"key is null or empty");
		return runOnRedis(new Function<Jedis,Integer>(){
			@Override
			public Integer apply(Jedis input) {
				return input.scard(key).intValue();
			}});
	}
	/**
	 * 获取{@code key}指定的集合的成员数
	 * @param key 变量名,不可为{@code null}或空
	 * @return
	 * @see {@link Jedis#scard(String)}
	 */
	public static int scard(String key){
		return scard(JedisPoolLazy.getDefaultInstance(), key);
	}
	/**
	 * 设置key指定变量的有效期
	 * @param jedisPoolLazy
	 * @param key 变量名,不可为{@code null}或空
	 * @param timeMills 有效期(毫秒), timestamp为true时timeMills为时间戳，否则timeMills为时间长度
	 * @param timestamp 
	 * @return 为true设置成功
	 */
	public static boolean expire(JedisPoolLazy jedisPoolLazy,final String key,final long timeMills,final boolean timestamp){
		checkArgument(!Strings.isNullOrEmpty(key),"key is null or empty");
		return runOnRedis(new Function<Jedis,Boolean>(){
			@Override
			public Boolean apply(Jedis input) {
				if(timestamp){
					return 1 == input.pexpireAt(key, timeMills);
				}
				else{
					return 1 == input.pexpire(key, timeMills);
				}
			}},jedisPoolLazy);
	}
	/**
	 * 设置key指定变量的有效期
	 * @param key 变量名,不可为{@code null}或空
	 * @param timeMills 有效期(毫秒), timestamp为true时timeMills为时间戳，否则timeMills为时间长度
	 * @param timestamp 
	 * @return
	 */
	public static boolean expire(String key,long timeMills,boolean timestamp){
		return expire(JedisPoolLazy.getDefaultInstance(),key,timeMills,timestamp);
	}
	/**
	 * 测试接参数是否有效,无效抛出异常
	 * @param props
	 */
	public static void checkConnect(Map<JedisPoolLazy.PropName,Object> input){
		Map<JedisPoolLazy.PropName,Object> props = JedisUtils.initParameters(input);
	    Jedis jedis = new Jedis(
	    		(String)props.get(JedisPoolLazy.PropName.host), 
	    		((Number)props.get(JedisPoolLazy.PropName.port)).intValue(), 
	    		((Number)props.get(JedisPoolLazy.PropName.timeout)).intValue(),
	    		((Number)props.get(JedisPoolLazy.PropName.timeout)).intValue());
	
	    try {
	    	jedis.connect();
	    	String password = (String)props.get(JedisPoolLazy.PropName.password);
	    	if (!Strings.isNullOrEmpty(password)) {
	    		jedis.auth(password);
	    	}
	    	int database = (int)props.get(JedisPoolLazy.PropName.database);
	    	if (database != 0) {
	    		jedis.select(database);
	    	}
	    } finally{
	    	jedis.close();
	    }
	}

	/**
	 * 测试接参数是否有效
	 * @return 连接失败返回{@code false}
	 */
	public static boolean testConnect(Map<JedisPoolLazy.PropName,Object> props){
		try {
			checkConnect(props);
			return true;
		} catch (JedisException je) {	      
		      return false;
		}
	}
	/**
	 * 测试{@link Jedis}连接是否有效<br>
	 * 测试方式参照 {@link redis.clients.jedis.JedisFactory#validateObject(org.apache.commons.pool2.PooledObject)}
	 * @param jedis 要测试的{@link Jedis}实例，为{@code null}时返回{@code false}
	 * @return 连接有效返回{@code true}，否则返回{@code false}
	 */
	public static boolean testConnect(Jedis jedis){
		return (jedis instanceof Jedis) && jedis.isConnected() && jedis.ping().equals("PONG");
	}
}
